#include "GameConfig.h"
#include "StringTools.h"
#include "AppDelegate.h"

Config::Config()
{
	xdoc = nullptr;
	setConfigScheme();
	loadConfig();
}

Config::~Config()
{
	if (xdoc)
	{
		saveConfig();
		delete xdoc;
	}
}

void Config::createDefaultConfigFile()
{
	xdoc = new tx::XMLDocument();
	xdoc->SetBOM(true);
	auto xdec = xdoc->NewDeclaration(NULL);
	xdoc->LinkEndChild(xdec);
	auto xroot = addElement(xdoc, "config");
	for (auto cfgclass : configScheme)
	{
		auto xcfgclass = addElement(xroot, cfgclass.classname);
		for (auto classitem : cfgclass.items)
		{
			addElement(xcfgclass, classitem.itemname, classitem.defaultvalue);
		}
	}
	saveConfig();
}

void Config::loadConfig()
{
	xdoc = new tx::XMLDocument();
	xdoc->SetBOM(true);
	xdoc->LoadFile(CFGFILE);
	if (xdoc->Error())
	{
		free(xdoc);
		createDefaultConfigFile();
	}
	checkConfigScheme();
}

void Config::saveConfig()
{
	auto fp = fopen(CFGFILE, "w+");
	xdoc->SaveFile(fp);
	fclose(fp);
}

int Config::getIntValue(char* cfgclass, char* cfgitem, char* attrname)
{
	return getElement(cfgclass, cfgitem)->IntAttribute(attrname);
}

float Config::getFloatValue(char * cfgclass, char * cfgitem, char * attrname)
{
	return getElement(cfgclass, cfgitem)->FloatAttribute(attrname);
}

const char * Config::getCfgValue(char * cfgclass, char * cfgitem, char * attrname)
{
	return getElement(cfgclass, cfgitem)->Attribute(attrname);
}

void Config::setIntValue(char * cfgclass, char * cfgitem, int attrval, char * attrname, bool autosave)
{
	getElement(cfgclass, cfgitem)->SetAttribute(attrname, attrval);
	if (autosave) saveConfig();
}

void Config::setFloatValue(char * cfgclass, char * cfgitem, float attrval, char * attrname, bool autosave)
{
	getElement(cfgclass, cfgitem)->SetAttribute(attrname, attrval);
	if (autosave) saveConfig();
}

void Config::setCfgValue(char * cfgclass, char * cfgitem, char * attrval, char * attrname, bool autosave)
{
	getElement(cfgclass, cfgitem)->SetAttribute(attrname, attrval);
	if (autosave) saveConfig();
}

void Config::setConfigScheme()
{
	auto graphics = addClassToConfigScheme("graphics");
	addClassItemToConfigScheme(graphics, "resw", "1280");
	addClassItemToConfigScheme(graphics, "wndratiow", "16");
	addClassItemToConfigScheme(graphics, "wndratioh", "9");
	addClassItemToConfigScheme(graphics, "msaa", "4");
	addClassItemToConfigScheme(graphics, "vsync", "0");
	addClassItemToConfigScheme(graphics, "fpslimit", "240");

	auto audio = addClassToConfigScheme("audio");
	addClassItemToConfigScheme(audio, "volmaster", "75");
	addClassItemToConfigScheme(audio, "volmusic", "75");

	auto gameplay = addClassToConfigScheme("gameplay");
	addClassItemToConfigScheme(gameplay, "hispeed", "2.0");
	addClassItemToConfigScheme(gameplay, "gamestyle", "0");
	addClassItemToConfigScheme(gameplay, "keyscanfreq", "1000");

	auto key = addClassToConfigScheme("keybind");
	addClassItemToConfigScheme(key, "4A1", strtool::int2str('D'));
	addClassItemToConfigScheme(key, "4A2", strtool::int2str('F'));
	addClassItemToConfigScheme(key, "4A3", strtool::int2str('J'));
	addClassItemToConfigScheme(key, "4A4", strtool::int2str('K'));
	addClassItemToConfigScheme(key, "5A1", strtool::int2str('D'));
	addClassItemToConfigScheme(key, "5A2", strtool::int2str('F'));
	addClassItemToConfigScheme(key, "5A3", strtool::int2str(' '));
	addClassItemToConfigScheme(key, "5A4", strtool::int2str('J'));
	addClassItemToConfigScheme(key, "5A5", strtool::int2str('K'));
	addClassItemToConfigScheme(key, "6A1", strtool::int2str('S'));
	addClassItemToConfigScheme(key, "6A2", strtool::int2str('D'));
	addClassItemToConfigScheme(key, "6A3", strtool::int2str('F'));
	addClassItemToConfigScheme(key, "6A4", strtool::int2str('J'));
	addClassItemToConfigScheme(key, "6A5", strtool::int2str('K'));
	addClassItemToConfigScheme(key, "6A6", strtool::int2str('L'));
	addClassItemToConfigScheme(key, "7A1", strtool::int2str('S'));
	addClassItemToConfigScheme(key, "7A2", strtool::int2str('D'));
	addClassItemToConfigScheme(key, "7A3", strtool::int2str('F'));
	addClassItemToConfigScheme(key, "7A4", strtool::int2str(' '));
	addClassItemToConfigScheme(key, "7A5", strtool::int2str('J'));
	addClassItemToConfigScheme(key, "7A6", strtool::int2str('K'));
	addClassItemToConfigScheme(key, "7A7", strtool::int2str('L'));
	addClassItemToConfigScheme(key, "8A1", strtool::int2str('A'));
	addClassItemToConfigScheme(key, "8A2", strtool::int2str('S'));
	addClassItemToConfigScheme(key, "8A3", strtool::int2str('D'));
	addClassItemToConfigScheme(key, "8A4", strtool::int2str('F'));
	addClassItemToConfigScheme(key, "8A5", strtool::int2str('J'));
	addClassItemToConfigScheme(key, "8A6", strtool::int2str('K'));
	addClassItemToConfigScheme(key, "8A7", strtool::int2str('L'));
	addClassItemToConfigScheme(key, "8A8", strtool::int2str(VK_OEM_1));
	addClassItemToConfigScheme(key, "4B1", strtool::int2str(VK_LSHIFT));
	addClassItemToConfigScheme(key, "4B2", strtool::int2str('Z'));
	addClassItemToConfigScheme(key, "4B3", strtool::int2str('S'));
	addClassItemToConfigScheme(key, "4B4", strtool::int2str('X'));
	addClassItemToConfigScheme(key, "5B1", strtool::int2str('Z'));
	addClassItemToConfigScheme(key, "5B2", strtool::int2str('S'));
	addClassItemToConfigScheme(key, "5B3", strtool::int2str('X'));
	addClassItemToConfigScheme(key, "5B4", strtool::int2str('D'));
	addClassItemToConfigScheme(key, "5B5", strtool::int2str('C'));
	addClassItemToConfigScheme(key, "6B1", strtool::int2str(VK_LSHIFT));
	addClassItemToConfigScheme(key, "6B2", strtool::int2str('Z'));
	addClassItemToConfigScheme(key, "6B3", strtool::int2str('S'));
	addClassItemToConfigScheme(key, "6B4", strtool::int2str('X'));
	addClassItemToConfigScheme(key, "6B5", strtool::int2str('D'));
	addClassItemToConfigScheme(key, "6B6", strtool::int2str('C'));
	addClassItemToConfigScheme(key, "7B1", strtool::int2str('Z'));
	addClassItemToConfigScheme(key, "7B2", strtool::int2str('S'));
	addClassItemToConfigScheme(key, "7B3", strtool::int2str('X'));
	addClassItemToConfigScheme(key, "7B4", strtool::int2str('D'));
	addClassItemToConfigScheme(key, "7B5", strtool::int2str('C'));
	addClassItemToConfigScheme(key, "7B6", strtool::int2str('F'));
	addClassItemToConfigScheme(key, "7B7", strtool::int2str('V'));
	addClassItemToConfigScheme(key, "8B1", strtool::int2str(VK_LSHIFT));
	addClassItemToConfigScheme(key, "8B2", strtool::int2str('Z'));
	addClassItemToConfigScheme(key, "8B3", strtool::int2str('S'));
	addClassItemToConfigScheme(key, "8B4", strtool::int2str('X'));
	addClassItemToConfigScheme(key, "8B5", strtool::int2str('D'));
	addClassItemToConfigScheme(key, "8B6", strtool::int2str('C'));
	addClassItemToConfigScheme(key, "8B7", strtool::int2str('F'));
	addClassItemToConfigScheme(key, "8B8", strtool::int2str('V'));

}

vector<Config::CFGCLASS>::iterator Config::addClassToConfigScheme(char * classname)
{
	CFGCLASS cls;
	cls.classname = classname;
	configScheme.push_back(cls);
	return --configScheme.end();
}

void Config::addClassItemToConfigScheme(vector<Config::CFGCLASS>::iterator cfgclass, char * itemname, char * defvalue)
{
	CFGITEM item;
	item.itemname = itemname;
	item.defaultvalue = defvalue;
	cfgclass->items.push_back(item);
}

void Config::addClassItemToConfigScheme(vector<CFGCLASS>::iterator cfgclass, char * itemname, string defvalue)
{
	char* cval = new char[defvalue.length()];
	strcpy(cval, defvalue.c_str());
	addClassItemToConfigScheme(cfgclass, itemname, cval);
}

void Config::checkConfigScheme()
{
	bool error = false;
	auto xroot = xdoc->FirstChildElement("config");
	if (xroot != NULL)
	{
		for (auto cfgclass : configScheme)
		{
			auto xclass = xroot->FirstChildElement(cfgclass.classname);
			if (xclass != NULL)
			{
				for (auto cfgitem : cfgclass.items)
				{
					auto xitem = getElement(cfgclass.classname, cfgitem.itemname);
					if (xitem == NULL)
					{
						error = true;
						xitem = addElement(xclass, cfgitem.itemname, cfgitem.defaultvalue);
					}
					auto xvalue = xitem->Attribute("value");
					if (xvalue == 0)
					{
						error = true;
						xitem->SetAttribute("value", cfgitem.defaultvalue);
					}
				}
			}
			else
			{
				createDefaultConfigFile();
				break;
			}
		}
	}
	else
	{
		createDefaultConfigFile();
		return;
	}
	if (error)
	{
		saveConfig();
	}
}

tx::XMLElement * Config::addElement(tx::XMLNode * target, char* name, char* attrval, char* attrname)
{
	auto element = xdoc->NewElement(name);
	if (attrval) element->SetAttribute(attrname, attrval);
	target->LinkEndChild(element);
	return element;
}

tx::XMLElement * Config::getElement(char * cfgclass, char * cfgitem)
{
	auto tag = xdoc->RootElement();
	tag = tag->FirstChildElement(cfgclass);
	tag = tag->FirstChildElement(cfgitem);
	return tag;
}

ConfigKeys::ConfigKeys()
{
	initKeySets();
}

ConfigKeys::~ConfigKeys()
{
}

void ConfigKeys::setActiveKeySet(int trackCount)
{
	activeKeySetIndex = trackCount - 4;
}

void ConfigKeys::setActiveType(int type)
{
	activeKeySetType = type;
}

KEYCODE * ConfigKeys::getKeyset(int keyCount, int type)
{
	if (type == 2) return keySetsB[keyCount - 4];
	return keySets[keyCount - 4];
}

KEYCODE * ConfigKeys::getActiveKeySet()
{
	if (activeKeySetType == 1) return keySets[activeKeySetIndex];
	else return keySetsB[activeKeySetIndex];
}

KEYCODE ConfigKeys::getTrackBindingKey(int trackCount)
{
	if (activeKeySetType == 1) return keySets[activeKeySetIndex][trackCount];
	else return keySetsB[activeKeySetIndex][trackCount];
}

void ConfigKeys::initKeySets()
{
	keySets = new KEYCODE*[5];
	keySets[0] = getKeycodeA(4);
	keySets[1] = getKeycodeA(5);
	keySets[2] = getKeycodeA(6);
	keySets[3] = getKeycodeA(7);
	keySets[4] = getKeycodeA(8);

	keySetsB = new KEYCODE*[5];
	keySetsB[0] = getKeycodeB(4);
	keySetsB[1] = getKeycodeB(5);
	keySetsB[2] = getKeycodeB(6);
	keySetsB[3] = getKeycodeB(7);
	keySetsB[4] = getKeycodeB(8);

}

KEYCODE * ConfigKeys::getKeycodeA(int keycount)
{
	KEYCODE* ret = new KEYCODE[keycount];
	for (int i = 0; i < keycount; i++)
	{
		char* itemname = new char[3];
		sprintf(itemname, "%dA%d", keycount, i + 1);
		ret[i] = pConfig->getIntValue("keybind", itemname);
	}
	return ret;
}

KEYCODE * ConfigKeys::getKeycodeB(int keycount)
{
	KEYCODE* ret = new KEYCODE[keycount];
	for (int i = 0; i < keycount; i++)
	{
		char* itemname = new char[3];
		sprintf(itemname, "%dB%d", keycount, i + 1);
		ret[i] = pConfig->getIntValue("keybind", itemname);
	}
	return ret;
}

